Categories
Vue

Vuelidate — Async Validation and Rules Data

Spread the love

Vue.js doesn’t come with any form validation capabilities by default.

Therefore, we need to add our own form validation library or with our own code.

In this article, we’ll look at how to validate forms with Vuelidate.

Asynchronous Validation

We can validate form fields asynchronously.

For example, we can write:

<template>
  <div id="app">
    <div :class="{ 'form-group--error': $v.$error }">
      <label>Name</label>
      <input v-model.trim="$v.name.$model">
    </div>
    <div v-if="!$v.name.required">Name is required.</div>
    <div v-if="!$v.name.isUnique">Name already exists.</div>
  </div>
</template>
<script>
import { required } from "vuelidate/lib/validators";

export default {
  name: "App",
  data() {
    return {
      name: ""
    };
  },
  validations: {
    name: {
      required,
      isUnique(value) {
        if (value === "") return true;
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(typeof value === "string" && value.length % 2 !== 0);
          }, 350 + Math.random() * 300);
        });
      }
    }
  }
};
</script>

We have the isUnique method that returns a promise that resolves to true if value is string and its length is even.

value is the value that we inputted.

true means the value is invalid.

In the template, we just check the rule with the $v.name.isUnique property.

We can also use the async and await syntax for async validation.

We just return the resolved value inside the function:

validations: {
  async isUnique (value) {
    if (value === '') return true
    const response = await fetch(`/api/unique/${value}`)
    return Boolean(await response.json())
  }
}

Delayed Validation Errors

We can delay validation errors.

To do that, we can create a touch method that is delayed.

For example, we can write:

<template>
  <div id="app">
    <div :class="{ 'form-group--error': $v.$error }">
      <label>Name</label>
      <input v-model.trim="$v.name.$model" @input="delayTouch($v.name)">
    </div>
    <div v-if="!$v.name.required">Name is required.</div>
    <div
      class="error"
      v-if="!$v.name.minLength"
    >Name must have at least {{$v.name.$params.minLength.min}} letters.</div>
    <div
      class="error"
      v-if="!$v.name.maxLength"
    >Name must have at most {{$v.name.$params.maxLength.max}} letters.</div>
  </div>
</template>
<script>
import { required, minLength, maxLength } from "vuelidate/lib/validators";
const touchMap = new WeakMap();

export default {
  name: "App",
  data() {
    return {
      name: ""
    };
  },
  validations: {
    name: {
      required,
      minLength: minLength(4),
      maxLength: maxLength(15)
    }
  },
  methods: {
    delayTouch($v) {
      $v.$reset();
      if (touchMap.has($v)) {
        clearTimeout(touchMap.get($v));
      }
      touchMap.set($v, setTimeout($v.$touch, 1000));
    }
  }
};
</script>

to add a delayTouch method to run the form validation code with a delay.

We have a weak map to check store the timer that runs $v.$touch with a delay.

If the timer exists in the weak map, we clear the timeout.

This way, we won’t have to create a new timer every time delayTouch is run.

delayTouch is run when the input event is emitted.

Accessing Validator Parameters

We can access the $params property of the child field.

For example, we can write:

<template>
  <div id="app">
    <div :class="{ 'form-group--error': $v.$error }">
      <label>Name</label>
      <input v-model.trim="$v.form.name.$model">
    </div>
    <div v-if="!$v.form.name.required">Name is required.</div>
    <div
      v-if="!$v.form.name.minLength"
    >Name must have at least {{$v.form.name.$params.minLength.min}} letters.</div>
    <div
      v-if="!$v.form.name.maxLength"
    >Name must have at most {{$v.form.name.$params.maxLength.max}} letters.</div>
  </div>
</template>
<script>
import { required, minLength, maxLength } from "vuelidate/lib/validators";

export default {
  name: "App",
  data() {
    return {
      form: {
        name: ""
      }
    };
  },
  validations: {
    form: {
      name: {
        required,
        minLength: minLength(4),
        maxLength: maxLength(15)
      }
    }
  }
};
</script>

We have the $v.form.name.$params.minLength.min and $v.form.name.$params.maxLength.max properties to access the min and max number of allowed characters for the name field.

Conclusion

We can validate form fields asynchronously and access form validation rules data with Vuelidate.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *